package com.fsy.its.netty.gbt.decoder;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
 * 国标数据包解析器
 * 输出：业务数据
 * +----------+-------+--------------------+
 * | 启动符    | 2字节  | ‘@@’，固定值64,64   |
 * | 业务数据  | 变长   | 控制单元和应用数据单元 |
 * | 校验和    | 1字节  | 业务数据算术校验和    |
 * | 结束符    | 2字节  | ‘##’，固定值35,35   |
 * +----------+-------+--------------------+
 */
@Slf4j
@Component
@Scope("prototype")
public class GbtFrameDecoder extends DelimiterBasedFrameDecoder {

    public static final int MAX_FRAME_LEN = 2048;
    public static final byte[] FRAME_START = {'@', '@'};
    public static final byte[] FRAME_END = {'#', '#'};

    public GbtFrameDecoder() {
        super(MAX_FRAME_LEN, Unpooled.copiedBuffer(FRAME_END));
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
        ByteBuf frame = (ByteBuf)super.decode(ctx, buffer);
        if( frame==null ) {
            return null;
        }
        log.info("Receive frame: {}", ByteBufUtil.hexDump(frame));

        //启动符
        int start = ByteBufUtil.indexOf(Unpooled.copiedBuffer(FRAME_START), frame);
        if( start < 0 ) {
            log.warn("Start delimiter {} not found.", new String(FRAME_START));
            return null;
        }
        frame.skipBytes(start+FRAME_START.length);

        //业务数据
        int dataLen = frame.readableBytes() - 1;
        if( dataLen <= 0 ) {
            return null;
        }
        ByteBuf data = frame.readRetainedSlice(dataLen);

        //校验和
//        short sum = frame.readUnsignedByte();
//        short checkSum = HexUtil.checkSum(ByteBufUtil.getBytes(data));
//        if( sum != checkSum ) {
//            log.error("Check sum error: sum={}, my sum={}", sum, checkSum);
//            return null;
//        }

        return data;
    }
}
